#ifndef __2865031_SMARTPOINTER_HH__
#define __2865031_SMARTPOINTER_HH__
//#define __DEBUG_MODE__
#include "exception.h"

#ifdef __DEBUG_MODE__
    #include <iostream>
#endif


template< typename T , T * __new(unsigned int), void __delete(T *), void __copy(T *,T **,unsigned int)>
class AbstractSmartPointer
{
    protected:

        unsigned int * cptr;
        T            * data;
        unsigned int * size;

    protected:


        inline void deleteObject()
        {
            if( cptr ) delete cptr;
            if( size ) delete size;
            __delete(data);
        }

        inline void allocObject(unsigned int _size)
        {
            cptr = new unsigned int(1);
            size = new unsigned int(_size);
            data = __new(_size);
        }

        inline void copyObject(T * src, unsigned int _size)
        {
            allocObject(_size);
            __copy(src,&data,_size);
        }

        inline void attachObject(const AbstractSmartPointer &asp)
        {
            cptr = asp.cptr;
            size = asp.size;
            data = asp.data;
            (*cptr)++;
        }

        inline void detachObject()
        {
            (*cptr)--;
            if( (*cptr) == 0 )
                deleteObject();
            cptr = 0;
            size = 0;
            data = 0;
        }

        inline void unsharObject()
        {
            if( (*cptr) > 1 )
            {
                (*cptr)--;
                copyObject(data,*size);
            }
        }

    public:

        AbstractSmartPointer() : cptr(0), data(0), size(0)
        {
            allocObject(0);
            #ifdef __DEBUG_MODE__
                std::cout <<"["<<this<<"] "<<"AbstractSmartPointer() ptr ["<<cptr<<"] = ("<<*cptr<<") new cptr"<< std::endl;
            #endif
        }

        virtual ~AbstractSmartPointer()
        {
            #ifdef __DEBUG_MODE__
                std::cout <<"["<<this<<"] "<<"~AbstractSmartPointer() ptr ["<<cptr<<"] = ("<<*cptr<<")"<< std::endl;
            #endif
            detachObject();
        }

        AbstractSmartPointer(const AbstractSmartPointer &asp) : cptr(0), data(0), size(0)
        {
            attachObject(asp);
            #ifdef __DEBUG_MODE__
                std::cout <<"["<<this<<"] "<<"AbstractSmartPointer(const AbstractSmartPointer &) ptr ["<<cptr<<"] = ("<<*cptr<<")"<< std::endl;
            #endif

        }

        virtual AbstractSmartPointer& operator=(const AbstractSmartPointer &asp)
        {
            detachObject();
            attachObject(asp);
            #ifdef __DEBUG_MODE__
                std::cout <<"["<<this<<"] "<<"AbstractSmartPointer& operator=(const AbstractSmartPointer &) ptr ["<<cptr<<"] = ("<<*cptr<<")"<< std::endl;
            #endif

            return (*this);
        }

        AbstractSmartPointer(unsigned int _size) : cptr(0), data(0), size(0)
        {
            allocObject(_size);
            #ifdef __DEBUG_MODE__
                std::cout <<"["<<this<<"] "<<"AbstractSmartPointer(unsigned int) ptr ["<<cptr<<"] = ("<<*cptr<<") new cptr"<< std::endl;
            #endif
        }

        inline unsigned int getCptr() const { return *cptr; }
        inline unsigned int getSize() const { return *size; }
        inline bool        isShared() const { return ((*cptr) > 1 ); }

        T * getData()
        {
            unsharObject();
            return data;
        }

        void setSize(unsigned int _size)
        {
            if( *size != _size )
            {
                detachObject();
                allocObject(_size);
            }
        }



};

template< typename T >
inline T * newT(unsigned int size)
{
    return new T[size];
}

template< typename T >
inline void delT(T * data)
{
    if( data ) delete [] data;
}

template< typename T >
inline void cpyT(T * src, T ** dst, unsigned int size)
{
    for(unsigned int n=0; n < size ; n++)
        *((*dst)++)=*(src++);
}

template< typename T >
class SmartPointer : public AbstractSmartPointer<T,newT,delT,cpyT>
{
    public:

        SmartPointer() : AbstractSmartPointer<T,newT,delT,cpyT>() {}
        SmartPointer(unsigned int _size) : AbstractSmartPointer<T,newT,delT,cpyT>(_size) {}
};


#endif
